Querying DataFrames
A filter is an index-aligned boolean Series.
mask = orders["amount"].ge(100) & orders["status"].isin(["paid", "shipped"])
large_orders = orders.loc[mask, ["customer_id", "amount", "status"]]
Predicate rules
- Parenthesize each comparison when combining with
&,|, or~. - Use
isinfor membership,betweenfor closed ranges, andisna/notnafor missingness. - Use
.str,.dt, and.cataccessors for typed string, datetime, and categorical operations. - Check the mask's index when it was built from another object; alignment can change the selected rows.
recent = orders.loc[orders["created_at"].between("2026-01-01", "2026-03-31")]
missing_customer = orders.loc[orders["customer_id"].isna()]
query
DataFrame.query can make long analytical expressions readable:
threshold = 100
result = orders.query("amount >= @threshold and status == 'paid'")
Use ordinary boolean expressions when column names are awkward, predicates are constructed dynamically, or normal Python debugging is more valuable than the compact syntax. Never treat a query string assembled from untrusted input as a security boundary.
Filter versus select
Filtering chooses rows by a predicate. .loc can simultaneously choose rows and
columns; .iloc chooses by positions. Keeping those ideas separate prevents
many shape and label mistakes.